package com.thinkit.bigdata.core.util.es.elasticsearch;

import com.google.common.base.Strings;
import com.thinkit.bigdata.core.util.PropertiesUtil;
import com.thinkit.bigdata.web.coremet.SystemStatic;
import com.thinkit.bigdata.web.model.es.countEs.CountModelRules;
import com.thinkit.bigdata.web.model.es.countEs.CountSource;
import org.apache.log4j.Logger;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsRequest;
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsResponse;
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
import org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.InternalAggregations;
import org.elasticsearch.search.aggregations.bucket.filter.InternalFilter;
import org.elasticsearch.search.aggregations.bucket.nested.InternalNested;
import org.elasticsearch.search.aggregations.bucket.terms.*;
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
import org.elasticsearch.search.sort.NestedSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import java.io.IOException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * es java api
 */

public class EsCrud {

    private static Logger logger = Logger.getLogger(EsCrud.class);

    private EsCrud() {}

    private Client client = null;

    private static  EsCrud esCrud = null;

    public EsCrud(Client client) {
        this.client = client;
    }

    public static EsCrud getInstance() throws UnknownHostException {
        if (esCrud == null) {
            synchronized (EsCrud.class) {
                if (esCrud == null) {
                    esCrud = new EsCrud(ClientFactory.transportClient());
                }
            }
        }
        return esCrud;
    }

    public void close() {
        if (client != null) {
            this.client.close();
            logger.info("与ES的连接已关闭");
        }
    }

    /**
     * 判断index是否存在
     *
     * @param index -
     * @return -
     */
    public boolean indexExists(String index) {
        IndicesExistsRequest request = new IndicesExistsRequest(index);
        IndicesExistsResponse response = client.admin().indices().exists(request).actionGet();
        if (response.isExists()) {
            return true;
        } else {
            logger.info(index + " 不存在");
            return false;
        }
    }

    /**
     * 判断type是否存在
     *
     * @param typeO -
     * @return -
     */
    public boolean typeExists(String index, String typeO) {
        String[] indices = {index};
        TypesExistsRequest request = new TypesExistsRequest(indices, typeO);
        TypesExistsResponse response = client.admin().indices().typesExists(request).actionGet();
        if (response.isExists()) {
            return true;
        } else {
            logger.info(index + "." + typeO + " 不存在");
            return false;
        }
    }

    /**
     * <pre>
     * 索引刷新方法
     *@param indexName 刷新索引
     *@return 是否刷新成功
     * <pre>
     */
    public boolean indexRefresh(String indexName ) {
        logger.info("ES索引开始刷新，索引名为：" + indexName);
        try {
            RefreshResponse response =  client.admin().indices()
                    .refresh(new RefreshRequest(indexName))
                    .actionGet();

            if(response.getShardFailures().length == response.getTotalShards()){
                logger.info("ES索引refresh刷新失败" +response.getShardFailures());
                return false;
            }else if(response.getShardFailures().length>0){
                logger.info("ES索引refresh刷新部分分片失败"+response.getShardFailures());
            }
            logger.info("ES索引refresh刷新成功");
            return true;
        }catch (Exception e) {
            logger.error("ES刷新refresh失败", e);
            return false;
        }
    }

    /**
     * <pre>
     * 索引Flush方法
     *@param indexName 刷新索引
     *@return 是否刷新成功
     * <pre>
     */
    public boolean indexFlush(String indexName ) {
        logger.info("ES索引名开始刷新，索引名为：" + indexName);
        try {
            FlushResponse response =  client.admin().indices()
                    .flush(new FlushRequest(indexName))
                    .actionGet();
            if(response.getShardFailures().length == response.getTotalShards()){
                logger.info("ES索引flush刷新失败"+response.getShardFailures());
                return false;
            }else if(response.getShardFailures().length>0){
                logger.info("ES索引flush刷新部分分片失败"+response.getShardFailures());
            }
            logger.info("ES索引flush刷新成功");
            return true;
        }catch (Exception e) {
            logger.error("ES刷新flush失败", e);
            return false;
        }
    }

    /**
     * 创建索引
     *
     * @param indexName -
     */
    public void createIndex(String indexName) {
        try {
            CreateIndexResponse indexResponse = this.client
                    .admin()
                    .indices()
                    .prepareCreate(indexName)
                    .get();
            logger.info(indexResponse.isAcknowledged());
            if (indexResponse.isAcknowledged())
                logger.info("索引 " + indexName + " 创建成功");
            else
                logger.info("索引 " + indexName + " 创建失败");
        } catch (ElasticsearchException e) {
            logger.info(e.getStackTrace());
        }
    }

    /**
     * 根据索引删除数据
     *
     * @param index -
     */
    public void deleteIndex(String index) {
        DeleteIndexResponse deleteIndexResponse =
                this.client
                        .admin()
                        .indices()
                        .prepareDelete(index)
                        .get();
        logger.info(deleteIndexResponse.isAcknowledged()); // true
    }


    public String get(String index, String type, String id) {
        GetResponse getResponse = this.client
                .prepareGet()
                .setIndex(index)
                .setType(type)
                .setId(id)
                .get();
        return getResponse.getSourceAsString();
    }

    /**
     * 创建 语音相关  es Mapping
     */
    private void createMapper(String index, String type) throws IOException {
        XContentBuilder mapping = XContentFactory.jsonBuilder()
                .startObject()
                .startObject("properties")
                .startObject("serialNumber").field("type", "keyword").endObject()
                .startObject("audioPath").field("type", "keyword").endObject()
                .startObject("sttPath").field("type", "keyword").endObject()

                //voiceInfo
                .startObject("voiceInfo")
                .startObject("properties")
                .startObject("fileSize").field("type", "long").endObject()
                .startObject("callStartTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("callEndTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("holdDuration").field("type", "long").endObject()
                .startObject("customerNum").field("type", "keyword").endObject()
                .startObject("seatNum").field("type", "keyword").endObject()
                .startObject("callDirection").field("type", "integer").endObject()
                .startObject("groupId").field("type", "keyword").endObject()
                .startObject("seatGroup").field("type", "keyword").endObject()
                .startObject("seatId").field("type", "keyword").endObject()
                .startObject("seatName").field("type", "keyword").endObject()
                .startObject("proPhoneNum").field("type", "keyword").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("province").field("type", "keyword").endObject()
                .startObject("provinceCode").field("type", "keyword").endObject()
                .startObject("isEachRecord").field("type", "keyword").endObject()
                .startObject("onHook").field("type", "keyword").endObject()
                .startObject("customerloc").field("type", "keyword").endObject()
                .startObject("customerLevel").field("type", "keyword").endObject()
                .startObject("satisfaction").field("type", "keyword").endObject()
                .startObject("dissatisfactionMsg").field("type", "keyword").endObject()
                .startObject("reCallFlag").field("type", "integer").endObject()
                .startObject("workId").field("type", "keyword").endObject()
                .startObject("sendMsg").field("type", "integer").endObject()
                .startObject("message").field("type", "keyword").endObject()
                .startObject("proStatus").field("type", "integer").endObject()
                .endObject()
                .endObject()

                //sttInfo
                .startObject("sttInfo")
                .startObject("properties")
                .startObject("sttStartTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("sttEndTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()

                //interrupted
                .startObject("interrupted").field("type", "nested")
                .startObject("properties")
                .startObject("start").field("type", "double").endObject()
                .startObject("end").field("type", "double").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .endObject()
                .endObject()

                //silences
                .startObject("silences").field("type", "nested")
                .startObject("properties")
                .startObject("channel").field("type", "integer").endObject()
                .startObject("start").field("type", "double").endObject()
                .startObject("end").field("type", "double").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .endObject()
                .endObject()

                //sttSentences
                .startObject("sttSentences").field("type", "nested")
                .startObject("properties")
                .startObject("channel").field("type", "integer").endObject()
                .startObject("start").field("type", "double").endObject()
                .startObject("end").field("type", "double").endObject()
                .startObject("centent").field("type", "keyword").endObject()
                .startObject("emotion").field("type", "integer").endObject()
                .startObject("speed").field("type", "double").endObject()
                .startObject("energy").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("keywords").field("type", "nested")
                .startObject("properties")
                .startObject("keywordStart").field("type", "double").endObject()
                .startObject("keywordEnd").field("type", "double").endObject()
                .startObject("keyword").field("type", "keyword").endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                //businessTags
                .startObject("callTags").field("type", "nested")
                .startObject("properties")
                .startObject("tagId").field("type", "long").endObject()
                .startObject("tagName").field("type", "keyword").endObject()
                .startObject("proType").field("type", "integer").endObject()
                .startObject("proValue").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .endObject()
                .endObject()

                //ruleInfos
                .startObject("ruleInfos").field("type", "nested")
                .startObject("properties")
                .startObject("ruleId").field("type", "long").endObject()
                .startObject("ruleName").field("type", "keyword").endObject()
                .startObject("processed").field("type", "integer").endObject()
                .startObject("ruleTimes").field("type", "integer").endObject()
                .startObject("ruleDetails").field("type", "nested")
                .startObject("properties")
                .startObject("detailNum").field("type", "keyword").endObject()
                .startObject("voiceSegments").field("type", "nested")
                .startObject("properties")
                .startObject("startTime").field("type", "double").endObject()
                .startObject("endTime").field("type", "double").endObject()
                .endObject()
                .endObject()
                .startObject("matchStatus").field("type", "integer").endObject()
                .startObject("proStatus").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("appealNote").field("type", "keyword").endObject()
                .startObject("appealTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("appealUserId").field("type", "long").endObject()
                .startObject("proNote").field("type", "keyword").endObject()
                .startObject("proTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("proUserId").field("type", "long").endObject()
                .startObject("isMissing").field("type", "integer").endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()

                //workScores
                .startObject("workScores").field("type", "nested")
                .startObject("properties")
                .startObject("workId").field("type", "long").endObject()
                .startObject("workName").field("type", "keyword").endObject()
                .startObject("preQuality").field("type", "integer").endObject()
                .startObject("proStatus").field("type", "integer").endObject()
                .startObject("workScore").field("type", "integer").endObject()
                .startObject("scoreNote").field("type", "keyword").endObject()
                .startObject("proUserId").field("type", "long").endObject()
                .startObject("proUserName").field("type", "keyword").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("updateTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("proFlag").field("type", "integer").endObject()
                .startObject("modelRules").field("type", "nested")
                .startObject("properties")
                .startObject("modelId").field("type", "long").endObject()
                .startObject("modelName").field("type", "keyword").endObject()
                .startObject("ruleId").field("type", "long").endObject()
                .startObject("useFor").field("type", "integer").endObject()
                .startObject("ruleName").field("type", "keyword").endObject()
                .startObject("fateful").field("type", "integer").endObject()
                .startObject("scorePerTime").field("type", "integer").endObject()
                .startObject("ruleScore").field("type", "integer").endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()

                //wordFrequency
                .startObject("wordFrequency").field("type", "nested")
                .startObject("properties")
                .startObject("keyword").field("type", "keyword").endObject()
                .startObject("count").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("originalWords").field("type", "keyword").endObject()
                .startObject("isKnown").field("type", "integer").endObject()
                .endObject()
                .endObject()

                //appeals
                .startObject("appeals").field("type", "nested")
                .startObject("properties")
                .startObject("classification").field("type", "keyword").endObject()
                .startObject("scene").field("type", "keyword").endObject()
                .startObject("proType").field("type", "integer").endObject()
                .startObject("proValue").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .endObject()
                .endObject()

                //testInfos
                .startObject("testInfos").field("type", "nested")
                .startObject("properties")
                .startObject("workId").field("type", "long").endObject()
                .startObject("workName").field("type", "keyword").endObject()
                .startObject("workScore").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("proUserId").field("type", "integer").endObject()
                .startObject("proTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("testModelRules").field("type", "nested")
                .startObject("properties")
                .startObject("modelRuleNum").field("type", "keyword").endObject()
                .startObject("modelId").field("type", "long").endObject()
                .startObject("modelName").field("type", "keyword").endObject()
                .startObject("ruleId").field("type", "long").endObject()
                .startObject("ruleName").field("type", "keyword").endObject()
                .startObject("useFor").field("type", "integer").endObject()
                .startObject("fateful").field("type", "integer").endObject()
                .startObject("voiceSegments").field("type", "nested")
                .startObject("properties")
                .startObject("startTime").field("type", "double").endObject()
                .startObject("endTime").field("type", "double").endObject()
                .endObject()
                .endObject()
                .startObject("ruleScore").field("type", "integer").endObject()
                .startObject("matchStatus").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("proNote").field("type", "keyword").endObject()
                .startObject("proTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("proUserId").field("type", "long").endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject();

        //index：索引名   type：类型名（可以自己定义）
        PutMappingRequest putmap = Requests.putMappingRequest(index).type(type).source(mapping);
        //为索引添加映射
        client.admin().indices().putMapping(putmap).actionGet();
    }

    /**
     * 创建 坐席质检结果汇总  es Mapping
     */
    public void createMapperByCount(String index, String type) throws IOException {
        XContentBuilder mapping = XContentFactory.jsonBuilder()
                .startObject()
                .startObject("properties")
                .startObject("id").field("type", "keyword").endObject()
                .startObject("province").field("type", "keyword").endObject()
                .startObject("workId").field("type", "long").endObject()
                .startObject("workName").field("type", "keyword").endObject()
                .startObject("monthlyAssess").field("type", "long").endObject()
                .startObject("cycleAssess").field("type", "keyword").endObject()
                .startObject("assStartDate").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("assEndDate").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("groupId").field("type", "keyword").endObject()
                .startObject("groupName").field("type", "keyword").endObject()
                .startObject("seatId").field("type", "keyword").endObject()
                .startObject("seatName").field("type", "keyword").endObject()
                .startObject("workScore").field("type", "integer").endObject()
                .startObject("audioSum").field("type", "integer").endObject()
                .startObject("inputTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .startObject("modelRules").field("type", "nested")
                .startObject("properties")
                .startObject("modelId").field("type", "long").endObject()
                .startObject("modelName").field("type", "keyword").endObject()
                .startObject("ruleId").field("type", "long").endObject()
                .startObject("ruleName").field("type", "keyword").endObject()
                .startObject("useFor").field("type", "integer").endObject()
                .startObject("fateful").field("type", "integer").endObject()
                .startObject("ruleTimes").field("type", "integer").endObject()
                .startObject("score").field("type", "integer").endObject()
                .startObject("updateTime").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").field("type", "date").endObject()
                .endObject()
                .endObject()
                .endObject()
                .endObject();

        //index：索引名   type：类型名（可以自己定义）
        PutMappingRequest putmap = Requests.putMappingRequest(index).type(type).source(mapping);
        //为索引添加映射
        client.admin().indices().putMapping(putmap).actionGet();
    }

    public void insertDataByCount(String index, String type, CountSource source) throws IOException {
        if (!indexExists(index)) {
            createIndex(index);
        }
        if (!typeExists(index, type)) {
            createMapperByCount(index, type);
        }
        XContentBuilder builder = XContentFactory.jsonBuilder()
                .startObject()
                .field("id", source.getId())
                .field("province", source.getProvince())
                .field("workId", source.getWorkId())
                .field("workName", source.getWorkName())
                .field("monthlyAssess", source.getMonthlyAssess())
                .field("cycleAssess", source.getCycleAssess())
                .field("assStartDate", source.getAssStartDate())
                .field("assEndDate", source.getAssEndDate())
                .field("groupId", source.getGroupId())
                .field("groupName", source.getGroupName())
                .field("seatId", source.getSeatId())
                .field("seatName", source.getSeatName())
                .field("workScore", source.getWorkScore())
                .field("inputTime", source.getInputTime())
                .field("audioSum", source.getAudioSum())
                .field("modelRules")
                .startArray();
        for (CountModelRules modelRules : source.getModelRules()) {
            builder.startObject()
                    .field("modelId", modelRules.getModelId())
                    .field("modelName", modelRules.getModelName())
                    .field("ruleId", modelRules.getRuleId())
                    .field("ruleName", modelRules.getRuleName())
                    .field("useFor", modelRules.getUseFor())
                    .field("fateful", modelRules.getFateful())
                    .field("ruleTimes", modelRules.getRuleTimes())
                    .field("score", modelRules.getScore())
                    .field("updateTime", modelRules.getUpdateTime())
                    .endObject();
        }
        builder.endArray()
                .endObject();
        client.prepareIndex(index, type, source.getId()).setSource(builder).get();
    }

    /**
     * QueryBuilder 多条件分页查询
     * 可设置分页参数
     * 返回当页结果列表，和总条数
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public List<String> queryMapQBuilderList(String index, String type, Integer from, Integer size,
                                                QueryBuilder queryBuilder, String field, String sortOrder
    ) {

        SearchRequestBuilder search =
                this.client.prepareSearch()
                        .setIndices(index)
                        .setTypes(type)
                        .setQuery(queryBuilder)
                        .setFrom(from)
                        .setSize(size);

        if (PropertiesUtil.getProperty("audio.es.index").equals(index)) {
            if (field.equals("serialNumber") && "desc".equals(sortOrder)) {
                search.addSort(field, SortOrder.DESC);
            } else if (field.equals("serialNumber") && "asc".equals(sortOrder)) {
                search.addSort(field, SortOrder.ASC);
            } else if (!Strings.isNullOrEmpty(field) && "desc".equals(sortOrder) && !field.equals("serialNumber") && !field.equals("workScores.workScore")) {
                search.addSort(field, SortOrder.DESC).addSort("serialNumber", SortOrder.DESC);
            } else if (!Strings.isNullOrEmpty(field) && "asc".equals(sortOrder) && !field.equals("serialNumber") && !field.equals("workScores.workScore")) {
                search.addSort(field, SortOrder.ASC).addSort("serialNumber", SortOrder.DESC);
            } else if (PropertiesUtil.getProperty("audio.es.index").equals(index) &&
                    (Strings.isNullOrEmpty(field) || field.equals("voiceInfo.province") || field.equals("voiceInfo.provinceCode"))
            ) {
                search.addSort("serialNumber", SortOrder.DESC);
            } else if (field.equals("workScores.workScore") && "asc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.ASC));
            } else if (field.equals("workScores.workScore") && "desc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.DESC));
            }
        } else {
            if (Strings.isNullOrEmpty(field)) {
                search.addSort("inputTime", SortOrder.DESC);
            } else if (!Strings.isNullOrEmpty(field) && "desc".equals(sortOrder) ) {
                search.addSort(field, SortOrder.DESC);
            } else if (!Strings.isNullOrEmpty(field) && "asc".equals(sortOrder)) {
                search.addSort(field, SortOrder.ASC);
            }
        }
        logger.info(search.toString());
        SearchResponse searchResponse = search.get();
        List<String> docList = new ArrayList<>();
        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit hit : searchHits) {
            docList.add(hit.getSourceAsString());
        }
        return docList;
    }

    /**
     * QueryBuilder 多条件分页查询
     * 可设置分页参数
     * 返回当页结果列表，和总条数
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public List<String> queryMapQBuilderRetRuleAndWork(
            String index, String type, Integer from, Integer size,
            QueryBuilder queryBuilder, String field, String sortOrder, String[] includes
    ) {
        SearchRequestBuilder search =
                this.client.prepareSearch()
                        .setIndices(index)
                        .setTypes(type)
                        .setQuery(queryBuilder)
                        .setFrom(from)
                        .setSize(size)
                        .setFetchSource(includes, new String[]{});

        if (PropertiesUtil.getProperty("audio.es.index").equals(index)) {
            if (field.equals("serialNumber") && "desc".equals(sortOrder)) {
                search.addSort(field, SortOrder.DESC);
            } else if (field.equals("serialNumber") && "asc".equals(sortOrder)) {
                search.addSort(field, SortOrder.ASC);
            } else if (!Strings.isNullOrEmpty(field) && "desc".equals(sortOrder) && !field.equals("serialNumber") && !field.equals("workScores.workScore") && !field.equals("workScores.proUserId")) {
                search.addSort(field, SortOrder.DESC).addSort("serialNumber", SortOrder.DESC);
            } else if (!Strings.isNullOrEmpty(field) && "asc".equals(sortOrder) && !field.equals("serialNumber") && !field.equals("workScores.workScore") && !field.equals("workScores.proUserId")) {
                search.addSort(field, SortOrder.ASC).addSort("serialNumber", SortOrder.DESC);
            } else if (PropertiesUtil.getProperty("audio.es.index").equals(index) &&
                    (Strings.isNullOrEmpty(field) || field.equals("voiceInfo.province") || field.equals("voiceInfo.provinceCode"))
            ) {
                search.addSort("serialNumber", SortOrder.DESC);
            }
            //TODO 数据内部字段排序需要特殊处理
            else if (field.equals("workScores.workScore") && "asc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.ASC));
            } else if (field.equals("workScores.workScore") && "desc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.DESC));
            } else if (field.equals("workScores.proUserId") && "desc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.DESC));
            } else if (field.equals("workScores.proUserId") && "asc".equals(sortOrder)) {
                NestedSortBuilder nestedSort = new NestedSortBuilder("workScores");
                search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.ASC));
            }
        } else {
            if (!Strings.isNullOrEmpty(field) && "desc".equals(sortOrder) ) {
                search.addSort(field, SortOrder.DESC);
            } else if (!Strings.isNullOrEmpty(field) && "asc".equals(sortOrder)) {
                search.addSort(field, SortOrder.ASC);
            } else {
                search.addSort("inputTime", SortOrder.DESC);
            }
        }
        logger.info(search.toString());
        SearchResponse searchResponse = search.get();
        List<String> docList = new ArrayList<>();
        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit hit : searchHits) {
            docList.add(hit.getSourceAsString());
        }
        return docList;
    }

    /**
     * QueryBuilder 多条件分页查询
     * 可设置分页参数
     * 返回当页结果列表，和总条数
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public List<String> queryMyAssess(
            String index, String type, Integer from, Integer size,
            QueryBuilder queryBuilder, String field, String sortOrder, String[] includes
    ) {
        //得分更新时间、质检任务名称、坐席姓名，倒序排列。
        SearchRequestBuilder search =
                this.client.prepareSearch()
                        .setIndices(index)
                        .setTypes(type)
                        .setQuery(queryBuilder)
                        .setFrom(from)
                        .setSize(size)
                        .setFetchSource(includes, new String[]{});
        if (!Strings.isNullOrEmpty(field)) {
            if ("desc".equals(sortOrder)) {
                if ("workName".equals(field)) {
                    search.addSort(field, SortOrder.DESC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("seatName", SortOrder.DESC);
                } else if ("seatName".equals(field)) {
                    search.addSort(field, SortOrder.DESC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("workName", SortOrder.DESC);
                } else if ("modelRules.updateTime".equals(field)) {
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("workName", SortOrder.DESC)
                            .addSort("seatName", SortOrder.DESC);
                } else {
                    search.addSort(field, SortOrder.DESC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("workName", SortOrder.DESC)
                            .addSort("seatName", SortOrder.DESC);
                }
            } else {

                if ("workName".equals(field)) {
                    search.addSort(field, SortOrder.ASC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("seatName", SortOrder.DESC);
                } else if ("seatName".equals(field)) {
                    search.addSort(field, SortOrder.ASC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("workName", SortOrder.DESC);
                } else if ("modelRules.updateTime".equals(field)) {
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort(field).setNestedSort(nestedSort).order(SortOrder.ASC))
                            .addSort("workName", SortOrder.DESC)
                            .addSort("seatName", SortOrder.DESC);
                } else {
                    search.addSort(field, SortOrder.ASC);
                    NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
                    search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                            .addSort("workName", SortOrder.DESC)
                            .addSort("seatName", SortOrder.DESC);
                }
            }
        } else {
            NestedSortBuilder nestedSort = new NestedSortBuilder("modelRules");
            search.addSort(SortBuilders.fieldSort("modelRules.updateTime").setNestedSort(nestedSort).order(SortOrder.DESC))
                    .addSort("workName", SortOrder.DESC)
                    .addSort("seatName", SortOrder.DESC);
        }
        logger.info(search.toString());
        SearchResponse searchResponse = search.get();
        List<String> docList = new ArrayList<>();
        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit hit : searchHits) {
            docList.add(hit.getSourceAsString());
        }
        return docList;
    }

    /**
     * QueryBuilder 多件
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public List<String> queryQBuilderByMes(String index, String type, Integer from, Integer size, QueryBuilder queryBuilder, String[] includes) {
        SearchRequestBuilder search =
                this.client.prepareSearch()
                        .setIndices(index)
                        .setTypes(type)
                        .setQuery(queryBuilder)
                        .setFrom(from)
                        .setSize(size)
                        .setFetchSource(includes, new String[]{});
        search.addSort("serialNumber", SortOrder.DESC)
        .addSort("voiceInfo.callStartTime", SortOrder.DESC)
        .addSort("voiceInfo.inputTime", SortOrder.DESC)
        .addSort("voiceInfo.provinceCode", SortOrder.DESC);
        logger.info(search.toString());
        SearchResponse searchResponse = search.get();
        List<String> docList = new ArrayList<>();
        SearchHits searchHits = searchResponse.getHits();
        for (SearchHit hit : searchHits) {
            docList.add(hit.getSourceAsString());
        }
        return docList;
    }

    /**
     * QueryBuilder 多件
     * 返回总条数
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public Integer queryQBuilderAllByMes(String index, String type, QueryBuilder queryBuilder) {
        SearchRequestBuilder search =
                this.client.prepareSearch()
                        .setIndices(index)
                        .setTypes(type)
                        .setQuery(queryBuilder)
                        .setSize(0);
        logger.info(search.toString());
        SearchResponse searchResponse = search.get();
        Long count = searchResponse.getHits().getTotalHits();
        return count.intValue();
    }

    public String formatCloumn(String cloumn, String indexType) {
        if (Strings.isNullOrEmpty(cloumn)) {
            return "";
        }
        if (PropertiesUtil.getProperty("audio.es.index").equals(indexType)) {
            switch (cloumn) {
                case "province":
                    cloumn = "voiceInfo.province";
                    break;
                case "provinceCode":
                    cloumn = "voiceInfo.provinceCode";
                    break;
                case "customerNumber":
                    cloumn = "voiceInfo.customerNum";
                    break;
                case "customerNum":
                    cloumn = "voiceInfo.customerNum";
                    break;
                case "seatGroup":
                    cloumn = "voiceInfo.seatGroup";
                    break;
                case "seatId":
                    cloumn = "voiceInfo.seatId";
                    break;
                case "seatName":
                    cloumn = "voiceInfo.seatName";
                    break;
                case "isEachRecord":
                    cloumn = "voiceInfo.isEachRecord";
                    break;
                case "inputTime":
                    cloumn = "voiceInfo.inputTime";
                    break;
                case "holdDuration":
                    cloumn = "voiceInfo.holdDuration";
                    break;
                case "callStartTime":
                    cloumn = "voiceInfo.callStartTime";
                    break;
                case "callEndTime":
                    cloumn = "voiceInfo.callEndTime";
                    break;
                case "callDirection":
                    cloumn = "voiceInfo.callDirection";
                    break;
                case "satisfaction":
                    cloumn = "voiceInfo.satisfaction";
                    break;
                case "proPhoneNum":
                    cloumn = "voiceInfo.proPhoneNum";
                    break;
                case "customerloc":
                    cloumn = "voiceInfo.customerloc";
                    break;
                case "onHook":
                    cloumn = "voiceInfo.onHook";
                    break;
                case "reCallFlag":
                    cloumn = "voiceInfo.reCallFlag";
                    break;
                case "customerSign":
                    cloumn = "voiceInfo.customerSign";
                    break;
                case "customerLevel":
                    cloumn = "voiceInfo.customerLevel";
                    break;
                case "workName":
                    cloumn = "workScores.workName";
                    break;
                case "proFlag":
                    cloumn = "workScores.proFlag";
                    break;
                case "proStatus":
                    cloumn = "workScores.proStatus";
                    break;
                case "workScore":
                    cloumn = "workScores.workScore";
                    break;
                case "preQuality":
                    cloumn = "workScores.preQuality";
                    break;
                case "updateTime":
                    cloumn = "workScores.updateTime";
                    break;
                case "proUserId":
                    cloumn = "workScores.proUserId";
                    break;
            }
        } else if (PropertiesUtil.getProperty("count.es.index").equals(indexType)) {
            switch (cloumn) {
                case "modelName":
                    cloumn = "modelRules.modelName";
                    break;
                case "ruleName":
                    cloumn = "modelName.ruleName";
                    break;
                case "useFor":
                    cloumn = "modelName.useFor";
                    break;
                case "fateful":
                    cloumn = "modelName.fateful";
                    break;
                case "ruleTimes":
                    cloumn = "modelName.ruleTimes";
                    break;
                case "score":
                    cloumn = "modelName.score";
                    break;
                case "updateTime":
                    cloumn = "modelRules.updateTime";
                    break;
            }
        }
        return cloumn;
    }


    public Map<String, Long> getTaggingAggSumY(String index, String type, BoolQueryBuilder queryBuilder, String inculd) {
        //聚合条件
        AggregationBuilder agg = AggregationBuilders
                .nested("sysTagsGroup", "sysTags")
                .subAggregation(
                        AggregationBuilders
                                .filter(
                                        "sysTags.tagName",
                                        QueryBuilders.prefixQuery("sysTags.tagName", inculd + "")
                                )
                                .subAggregation(
                                        AggregationBuilders
                                                .nested("next", "sysTags.tagDetails")
                                                .subAggregation(
                                                        AggregationBuilders
                                                                .sum("matchStatus_count_1")
                                                                .field("sysTags.tagDetails.matchStatus")
                                                )
                                )
                );
        // 搜索条件
        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
        searchRequestBuilder.setIndices(index);
        searchRequestBuilder.setTypes(type);
        searchRequestBuilder.setQuery(queryBuilder);
        searchRequestBuilder.addAggregation(agg);
        searchRequestBuilder.setSize(0);
        // 结果获取执行
        SearchResponse searchResponse = searchRequestBuilder.get();

        InternalNested nested = searchResponse.getAggregations().get("sysTagsGroup");

        InternalAggregations aggregations = nested.getAggregations();

        InternalFilter aggregation = aggregations.get("sysTags.tagName");

        InternalAggregations aggregations1 = aggregation.getAggregations();

        InternalNested aggregations2 = aggregations1.get("next");

        InternalAggregations aggregations3 = aggregations2.getAggregations();

        Sum sum = aggregations3.get("matchStatus_count_1");

        Map<String, Long> result = new HashMap<>();
        result.put("allTagNum", aggregations2.getDocCount());
        result.put("errorTagNum", (long) sum.getValue());
        return result;
    }

    public Map<String, Double> getTaggingAggSum(String index, String type, BoolQueryBuilder queryBuilder, int size, String inculd) {
        //聚合条件
        TermsAggregationBuilder agg1 = AggregationBuilders
                .terms("sysTags.tagName")
                .field("sysTags.tagName")
                .size(size)
                .subAggregation(
                        AggregationBuilders.nested("detailsN", "sysTags.tagDetails")
                                .subAggregation(
                                        AggregationBuilders
                                                .terms("matchError")
                                                .field("sysTags.tagDetails.matchStatus")
                                                .subAggregation(
                                                        AggregationBuilders.sum("errorCount").field("sysTags.tagDetails.matchStatus")
                                                )
                                )
                );
        if (!Strings.isNullOrEmpty(inculd)) {
            agg1.includeExclude(new IncludeExclude(inculd, null));
        }
        AggregationBuilder agg = AggregationBuilders
                .nested("sysTagsGroup", "sysTags")
                .subAggregation(agg1);
        // 搜索条件
        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
        searchRequestBuilder.setIndices(index);
        searchRequestBuilder.setTypes(type);
        searchRequestBuilder.setQuery(queryBuilder);
        searchRequestBuilder.addAggregation(agg);
        searchRequestBuilder.setSize(0);
        // 结果获取执行
        double allTagNum = 0.0;
        double errorTagNum = 0.0;
        SearchResponse searchResponse = searchRequestBuilder.get();

        InternalNested nested = searchResponse.getAggregations().get("sysTagsGroup");

        InternalAggregations aggregations = nested.getAggregations();

        StringTerms aggregation = aggregations.get("sysTags.tagName");

        List<StringTerms.Bucket> list =  aggregation.getBuckets();

        for (StringTerms.Bucket bucket : list) {

            InternalNested nested1 = bucket.getAggregations().get("detailsN");

            InternalAggregations aggregations1 = nested1.getAggregations();

            LongTerms aggregation1 = aggregations1.get("matchError");

            List<LongTerms.Bucket> list1 =  aggregation1.getBuckets();

            for (LongTerms.Bucket bucket1 : list1) {

                allTagNum += bucket1.getDocCount();

                Sum sum = bucket1.getAggregations().get("errorCount");

                errorTagNum += sum.getValue();
            }
        }
        Map<String, Double> result = new HashMap<>();
        result.put("allTagNum", allTagNum);
        result.put("errorTagNum", errorTagNum);
        return result;
    }

    /**
     * 根据条件聚合 sum
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public Double getAggSum(String index, String type, BoolQueryBuilder queryBuilder, String colum, String filed) {
        //聚合条件
        AggregationBuilder agg = AggregationBuilders.sum(colum).field(filed);
        // 搜索条件
        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
        searchRequestBuilder.setIndices(index);
        searchRequestBuilder.setTypes(type);
        searchRequestBuilder.setQuery(queryBuilder);
        searchRequestBuilder.addAggregation(agg);
        searchRequestBuilder.setSize(0);
        // 执行
        SearchResponse searchResponse = searchRequestBuilder.get();
        Sum sum = searchResponse.getAggregations().get(colum);
        return sum.getValue();
    }

    /**
     * 根据条件聚合 sum
     *
     * @param index -
     * @param type  -
     * @return -
     */
    public Terms getTermsNum(String index, String type, BoolQueryBuilder queryBuilder, String colum, String filed) {
        //聚合条件
        AggregationBuilder agg = AggregationBuilders
                .terms(colum)
                .field(filed)
                .size(this.queryQBuilderAllByMes(SystemStatic.AUDIO_ES_INDEX, SystemStatic.AUDIO_ES_TYPE, queryBuilder));
        // 搜索条件
        SearchRequestBuilder searchRequestBuilder = client.prepareSearch();
        searchRequestBuilder.setIndices(index);
        searchRequestBuilder.setTypes(type);
        searchRequestBuilder.setQuery(queryBuilder);
        searchRequestBuilder.addAggregation(agg);
        searchRequestBuilder.setSize(0);
        // 执行
        SearchResponse searchResponse = searchRequestBuilder.get();
        return searchResponse.getAggregations().get(colum);
    }

    /**
     * 更新对应id 的索引数据，支持局部更新，根据es的配置时间来刷新
     *
     * @param indexname 索引库名
     * @param type      索引类型
     * @param jsondata  json格式的数据（待更新数据）
     * @param id        索引对象单独指定ID
     */
    public RestStatus updateIndex(String indexname, String type, String jsondata, String id) {
        UpdateResponse updateResponse = this.client.prepareUpdate(indexname, type, id)
                .setDoc(jsondata, XContentType.JSON).get();
        logger.info("serialNumber = " + id + " 更新【" + updateResponse.status() + "】");
        return updateResponse.status();
    }

    /**
     * 更新对应id 的索引数据，支持局部更新, 实时刷新(refresh)
     *
     * @param indexname 索引库名
     * @param type      索引类型
     * @param jsondata  json格式的数据（待更新数据）
     * @param id        索引对象单独指定ID
     */
    public RestStatus updateIndexRefreshByNow(String indexname, String type, String jsondata, String id) {
        UpdateRequest request = new UpdateRequest(indexname, type, id).doc(jsondata, XContentType.JSON);
        request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
        UpdateResponse updateResponse = this.client.update(request).actionGet();
        return updateResponse.status();
    }

}
